Extreme PALS Game Framework

Getting right into development mess

In the previous guide we added jumping.. Kind of… Except the character flies up and never comes back down.

In this guide, we will make jumping smooth and implement gravity into the game, so that the character can fall too instead of floating about forever.

Time to defy gravity - decyphering how jumping works

If you have followed the previous guide, you will have added the functionality, so that whenever the jump key is pressed, boy.startJumping() is called.

Let’s dig deeper and find out what this function does. Open up Boy.java and find the startJumping function. It should look like this:

// Called every time the player presses the jump key
public void startJumping() {
    if (currentY - DISPLACEMENT >= 0) {
        currentY -= DISPLACEMENT;
        boundingBox.setLocation(currentX, currentY);
    }
}

What does it do? It uses member variables currentY, DISPLACEMENT, boundingBox and currentX. To find out what these are, scroll back up to the top of the Boy class, to find where they are declared. You will find these lines:

// The current position of the character
private int currentX;
private int currentY;
// DISPLACEMENT is the distance covered by a single step of the character
private static final int DISPLACEMENT = 4;
// The boundingBox (sometimes called hit box) is a rectangle around the character
// It defines the space occupied by the character at the specific moment
// Used for detecting collisions
private Rectangle boundingBox;

Game coordinate grid

In a coordinate system like this, low X coordinates are closer to the left side of the screen and high X coordinates are towards the right side. Respectively, low Y coordinates are closer to the top of the screen and high are closer to the bottom.

Knowing what all these member variables are for, let’s get back to figuring out what the startJumping function does every time it is called (when the jump key is pressed).

First, it checks if (currentY - DISPLACEMENT >= 0). Subtracting DISPLACEMENT from currentY means moving the character’s vertical position up by DISPLACEMENT pixels. The check ensures that the character only moves up if the move does not go beyond the top of the game screen. If this condition is satisfied, currentY and the boundingBox are updated.

In an intuitive sense, there should be a counterpart for the jumping function - a falling function that gradually increases currentY while the character is in mid-air.

Getting back to earth - implementing falling

Scroll a little down to find the handleFalling function. It should look like this:

public void handleFalling() {
    // Skip falling altogether if the character is jumping
    if (jumping) {
        return;
    }

    int currentRow = (int) (currentY / Settings.TILE_SIZE);

    // If the character falls to the bottom of the world map - instant death
    if (currentRow + 1 >= World.rows) {
        die();
        return;
    }

    // Since the character is wider than one tile but less wide than two
    // Check the two tiles below the character
    int lowLeftX = (int) boundingBox.getMinX() + 1;
    int lowRightX = (int) boundingBox.getMaxX() - 1;
    int lowLeftCol = lowLeftX / Settings.TILE_SIZE;
    int lowRightCol = lowRightX / Settings.TILE_SIZE;

    Block lowLeftBlock = null;

    if (lowLeftCol < World.cols) {
        lowLeftBlock = World.map[currentRow + 1][lowLeftCol];
    }

    Block lowRightBlock = null;

    if (lowRightCol < World.cols) {
        lowRightBlock = World.map[currentRow + 1][lowRightCol];
    }

    // If both of the tiles below the character are thin air or beyond map edge
    // Make the character fall down DISPLACEMENT units
    if ((lowLeftBlock == null || lowLeftBlock.empty())
    && (lowRightBlock == null || lowRightBlock.empty())) {
        falling = true;
        currentY += DISPLACEMENT;
        boundingBox.setLocation(currentX, currentY);
    } else {
        falling = false;
    }
}

Read it through thoroughly - hopefully the code comments will help to understand what each part does. To summarise, this function moves the character down by displacement units if the blocks below it are air.

After taking a good look, see if you can answer the question - when should this function be called?

The answer is… Don’t just read on ~ make sure you’ve really thought about it before continuing ヽ( ͡°╭͜ʖ╮͡° )ノ

Okay, so the answer is, it should be called continuously while the game is running - we want the character to keep falling until they reach ground (or hit the bottom of the world, which leads to death in the given framework. You’re totally free to change this behaviour though ( ͡° ͜ʖ ͡ -) ).

To do this, open GameManager.java again and this time find the run function. Inside it you will find a line that calls boy.handleFalling(). Uncomment it and try running the game now. What happens?

Now we seem to have broken jumping again.. Whenever you press the jump button, the character should move up just a tiny bit and immediately land back down.

Let’s do this right - fixing up jumping

Take a good look at the handleFalling function again. The first lines say it should skip falling if the character is currently jumping, by checking if (jumping).

That’s a new member variable we haven’t seen before. If you scroll up to the member variable declarations, you will find these lines:

// jumping is 'true' when the character is jumping
// Is 'false' when the character is falling or on the ground
private boolean jumping;

Q: When should jumping be set to true? A: Whenever the character jumps!

Q: And where does this happen? A: In the startJumping function!

Could it be as simple as setting jumping to true in this function? Let’s modify it and see what happens! Try modifying the function so it looks like this:

// Called every time the player presses the jump key
public void startJumping() {
    if (currentY - DISPLACEMENT >= 0) {
        jumping = true;
        currentY -= DISPLACEMENT;
        boundingBox.setLocation(currentX, currentY);
    }
}

Try running the game now. You will see that we can jump, yay! But falling doesn’t work.. Again… What a puzzle!

Why?

Well, we set jumping to true, which now means that falling gets skipped. And jumping is never set to false, so it’s as if falling wasn’t there at all! We need to set it to false at some point, but when?

This is where the concept of keeping a jump counter helps out. What if, when you pressed the jump key, the character would start jumping for some amount of time and afterwards begin falling.

Let’s modify the startJumping function first by adding in the jump counter. The jump_count member variable is there exactly for this purpose. You are encouraged to scroll up to its declaration to find a more in-depth explanation of how it works.

The updated startJumping function looks like this:

// Called every time the player presses the jump key
public void startJumping() {
    if (currentY - DISPLACEMENT >= 0) {
        jumping = true;
        currentY -= DISPLACEMENT;
        boundingBox.setLocation(currentX, currentY);

        jump_count = 0;
    }
}

Now we will need to update this jump counter continuously until reaching a specific threshold and then set jumping to false. The threshold is the JUMP_COUNTER_THRESH member variable. Its value is quite arbitrary and you are more than welcome to play around with it to see what changing it does, once we get jumping to work properly. The updating of the jump counter will be the responsibility of the handleJumping function in Boy.java. Find it - it should look like this:

// Increments the jumping counter and moves character up if jumping
// Check the comments above 'jumping' and 'jump_count' variables
// For more details
public void handleJumping() {
    if (jumping) {
        jump_count++;

        if (jump_count >= JUMP_COUNTER_THRESH){
            jumping = false;
            jump_count = 0;
            falling = true;
        }

        checkBlockCollisions();
    }
}

When should this function be called? You might even recall seeing it commented out before. Just like we saw with the handleFalling function, when something needs to be done gradually, we turn to the run function in GameManager.java. In it, you will find a line calling boy.handleJumping() that is commented out. Uncomment it and try running the game now. What happens?

Falling should actually work! It looks very janky, but finally, all this effort has paid off and we got it to work at least somewhat.

Here is a gif of the character happily jumping to their demise ( ‾ ʖ̫ ‾)

Jumping in game

A nice finish - smoothing out jumping

The thing that is making jumping look so strange is that whenever the jump key is pressed - the character moves up by DISPLACEMENT pixels and stays in the air until the jump_count hits the JUMP_COUNTER_THRESH threshold, at which point it begins falling back down.

A much more natural approach is that the character should gradually move up bit by bit, and then begin gradually falling back down.

To achieve this, let’s first modify the startJumping function, by eliminating the instant jump up by DISPLACEMENT units. We can also throw in a little fix to disallow mid-air jumps by adding a check for jumping and falling to both be false first. (This is the part you may want to question later, if you decide to implement mid-air jumps). The result should look like this:

// Called every time the player presses the jump key
// Does nothing if the character is already jumping or falling
public void startJumping() {
    if (!jumping && !falling) {
        jumping = true;

        // Reinitialise the jump_count, useful to determine for how
        // Much time the character is going to stay in the air
        jump_count = 0;
    }
}

Now it’s time to modify handleJumping as well, to add that gradual movement up while jumping. The resulting modification could be like this:

// Increments the jumping counter and moves character up if jumping
// Check the comments above 'jumping' and 'jump_count' variables
// For more details
public void handleJumping() {
    if (jumping) {
        if (jump_count < JUMP_COUNTER_THRESH
            && currentY - DISPLACEMENT >= 0) {
            currentY -= DISPLACEMENT;
            boundingBox.setLocation(currentX, currentY);
        }

        jump_count++;

        if (jump_count >= JUMP_COUNTER_THRESH){
            jumping = false;
            jump_count = 0;
            falling = true;
        }

        checkBlockCollisions();
    }
}

Try running the game now! How does it feel? Jumping should be much smoother. You can play around with the values of DISPLACEMENT and JUMP_COUNTER_THRESH to see how they affect the result. For example, it could look something like this:

Jumping in game

Congratulations

You have survived this whole development mess and come out victorious, with a new implemented feature! We are all so proud. Here is your certificate of achievement:

Certificate of achievement -small

Now, make sure all your teammates are up to speed and that you have all managed to implement jumping in your branches of the game project. The path forward will diverge as you will need to split up the work in implementing the rest of the game - there is still much to do! You will need to decide amongst your teammates, who would prefer to work in which area between game development (adding new features much like in this guide), game design (an all new area about changing the game maps and structure) and graphic design (the game definitely needs some prettifying - for terrain, backgrounds and even adding animations).

When everyone is ready, follow through the portal back to the guide index page. From there, each member of the team should focus on a particular strand of guides based on the area they picked. (It’s okay for multiple people to pick the same things to work on, just make sure that no area gets overlooked).

Portal to guide index page